local Archy = LibStub("AceAddon-3.0"):GetAddon("Archy")
local Digsite = Archy:GetModule("Digsite", true) or Archy:NewModule("Digsite", "AceEvent-3.0", "AceTimer-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("Archy")
local floor = floor
local db
local eventsRegistered = false
local lastSite, sites


function Digsite:OnInitialize()
	db = Archy.db.profile
	self.frame = self:CreateListFrame()
	
	-- keeping the dig site blacklist on the character db due to dig sites being character based
	if not Archy.db.char.digsites then Archy.db.char.digsites = {} end
	if not Archy.db.char.digsites.blacklist then Archy.db.char.digsites.blacklist = {} end
	if not Archy.db.char.digsites.counter then Archy.db.char.digsites.counter = {} end
	
	-- setup the dig site survey database
	if not Archy.db.global.surveyNodes then 
		Archy.db.global.surveyNodes = {} 
	end
	if Archy.db.global.digsites then
		self:ImportOldDatabase()
	end
	self.nearestSite = nil
end



function Digsite:ImportOldDatabase()
	local olddb = Archy.db.global.digsites
	if not olddb then return end

	for name, oldSite in pairs(olddb) do
		if oldSite.nodes then
			local siteData = Archy.archData:GetSiteInfo(name)
			if siteData then
				for _, node in pairs(oldSite.nodes) do
					self:AddSurveyNode(siteData.blob, node.m, node.f, node.x, node.y)
				end
			end
		end
	end
	olddb, Archy.db.global.digsites = nil, nil
end

function Digsite:AddSurveyNode(blobID, m, f, x, y)
	local newNode = { m = m, f = f, x = x, y = y }
	local exists = false
	
	if not Archy.db.global.surveyNodes[blobID] then Archy.db.global.surveyNodes[blobID] = {} end
	for _, node in pairs(Archy.db.global.surveyNodes[blobID]) do	
		local distance = Archy.astrolabe:ComputeDistance(newNode.m, newNode.f, newNode.x, newNode.y, node.m, node.f, node.x, node.y)
		if not distance or IsInInstance() then distance = 0 end
		if distance <= 10 then 
			exists = true 
			break
		end
	end
	if not exists then
--		print("Adding survey node: " .. m .. " " .. f .. " " .. x .. " " .. y .. " to blob: " .. blobID)
		tinsert(Archy.db.global.surveyNodes[blobID], newNode)
		self:SendMessage("ArchySurveyNodeAdded")
	end
end

function Digsite:ShowHideList()
	if not db.general.show or not db.digsites.show or not sites or #sites == 0 or not self.visible then
		self.frame:Hide()
	else
		self.frame:Show()
	end
end

function Digsite:OnEnable()
	db = Archy.db.profile
	self:UpdateColors()
	self.visible = true

	self:RegisterArchaeologyEvents()
	self.updateTimer = self:ScheduleRepeatingTimer("Tick", 0.1)
end

function Digsite:OnDisable()
	self:UnregisterArchaeologyEvents()
	if self.updateTimer then
		self:CancelTimer(self.updateTimer)
	end
end

function Digsite:RegisterArchaeologyEvents()
	Archy.archData.RegisterCallback(self, "ArchDataTablesLoaded", "OnArchaeologyDataLoaded")
	Archy.archData.RegisterCallback(self, "ArchNodeFound", "OnNodeFound")
	Archy.archData.RegisterCallback(self, "ReceivedKeystone", "OnReceiveKeystone")
	Archy.archData.RegisterCallback(self, "ReceivedFragments", "OnReceiveFragments")
	Archy.archData.RegisterCallback(self, "DigSitesUpdated", "OnDigSitesUpdated")
	Archy.archData.RegisterCallback(self, "PlayerCastSurvey", "OnPlayerSurveyed")
	Archy.archData.RegisterCallback(self, "MapChanged", "OnMapChanged")
	self:RegisterEvent("PLAYER_ENTERING_WORLD", "OnDigSitesUpdated")
	self:RegisterMessage("ArchyConfigChanged", "OnConfigChanged")
	self:RegisterMessage("ArchyColorChanged", "OnColorChanged")
	self:RegisterMessage("ArchyStealthMode", "OnStealthMode")
	self:RegisterMessage("ArchyPositionReset", "OnResetPosition")
	eventsRegistered = true
end

function Digsite:UnregisterArchaeologyEvents()
	Archy.archData.UnregisterCallback(self, "ArchDataTablesLoaded")
	Archy.archData.UnregisterCallback(self, "ArchNodeFound")
	Archy.archData.UnregisterCallback(self, "ReceivedKeystone")
	Archy.archData.UnregisterCallback(self, "ReceivedFragments")
	Archy.archData.UnregisterCallback(self, "DigSitesUpdated")
	Archy.archData.UnregisterCallback(self, "PlayerCastSurvey")
	Archy.archData.UnregisterCallback(self, "MapChanged")
	self:UnregisterEvent("PLAYER_ENTERING_WORLD")
	self:UnregisterMessage("ArchyConfigChanged")
	self:UnregisterMessage("ArchyColorChanged")
	self:UnregisterMessage("ArchyStealthMode")
	self:UnregisterMessage("ArchyPositionReset")
	eventsRegistered = false
end

function Digsite:OnResetPosition()
	db = Archy.db.profile
	self.frame:ClearAllPoints()
	self.frame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", db.artifacts.location.x, db.artifacts.location.y)
end


function Digsite:OnStealthMode()
	self.visible = not self.visible
	self:ShowHideList()
end

function Digsite:OnColorChanged()
	self:UpdateColors()
end

function Digsite:OnConfigChanged()
	db = Archy.db.profile
	self:SetScale()
	self:SetAlpha()
	self:UpdateListFrame()
	self:UpdateColors()
	self:ShowHideList()
end

function Digsite:SetScale()
	self.frame:SetScale(db.digsites.scale)
end

function Digsite:SetAlpha()
	self.frame:SetAlpha(db.digsites.alpha)
end

function Digsite:ResetDigCounter(a, b)
	if not a or not b then return end
	for _, siteA in pairs(a) do
		local exists = false
		for _, siteB in pairs(b) do
			if siteA.name == siteB.name then
				exists = true
				break
			end
		end
		
		if not exists then
			self:ResetCounter(siteA.name)
		end
	end
end

function Digsite:ResetCounter(name)
	Archy.db.char.digsites.counter[name] = 0
end

function Digsite:IncrementCounter(name)
	Archy.db.char.digsites.counter[name] = Archy.db.char.digsites.counter[name] + 1
end

function Digsite:OnArchaeologyDataLoaded()
	self:OnDigSitesUpdated()
end

function Digsite:OnDigSitesUpdated()
	local newSites = Archy.archData:GetContinentSites(GetCurrentMapContinent(), true)
	if newSites then
		self:ResetDigCounter(sites, newSites)
		self:ResetDigCounter(newSites, sites)
	end
	
	sites = newSites
	self:UpdateListFrame()
	self:ShowHideList()
end

function Digsite:OnNodeFound(event, race, position, site)
--	print("Node found for ", race.name, " at ", floor(position.x*10000)/100, ", ", floor(position.y*10000)/100)
	self:IncrementCounter(site.name)
	if position then
		Digsite:AddSurveyNode(site.blob, position.map, position.floor, position.x, position.y)
	end
end

function Digsite:OnReceiveFragments(event, race, amount)
end
function Digsite:OnReceiveKeystone(event, race, amount)
end
function Digsite:OnPlayerSurveyed(event, position, site)
end
function Digsite:OnMapChanged(event, count)
end

function Digsite:AnnounceNearest()
	if self.nearestSite then
		Archy:Print("Nearest Dig Site is: ", self.nearestSite.name, " in " , self.nearestSite.zone.name)
	end
end

function Digsite:Tick()
	self:UpdateSiteDistances()
end

function Digsite:UpdateSiteDistances()
	if not sites then return end
	for _,site in pairs(sites) do
		site.distance = Archy.archData:DistanceToSite(site.name)
	end
	
	if db.digsites.showDistance or db.digsites.sortByDistance then
		self:UpdateListFrame()
	end
end

function Digsite:IsSiteBlacklisted(name)
	if Archy.db.char.digsites.blacklist[name] then return true else return false end
end

local siteWidth = 1
local zoneWidth = 1
local distWidth = 1
local digWidth = 1
function Digsite:UpdateListFrame()
	if not sites then return end
	
	local distance
	local nearest
	for _, site in pairs(sites) do
		if not site.distance then site.distance = Archy.archData:DistanceToSite(site.name) end
		if not self:IsSiteBlacklisted(site.name) then
			if not distance or site.distance < distance then 
				distance = site.distance 
				nearest = site
			end
		end
	end
	if nearest and (not self.nearestSite or self.nearestSite.name ~= nearest.name) then
		self.nearestSite = nearest
		self:SendMessage("ArchyNearestDigSiteChanged", self.nearestSite)
	end
	
	if db.digsites.sortByDistance then
		table.sort(sites, function(a,b)
			if Digsite:IsSiteBlacklisted(a.name) and not Digsite:IsSiteBlacklisted(b.name) then 
				return 1 < 0
			elseif not Digsite:IsSiteBlacklisted(a.name) and Digsite:IsSiteBlacklisted(b.name) then 
				return 0 < 1
			end
			if (a.distance == -1 and b.distance == -1) or (not a.distance and not b.distance) then
				return a.zone.name .. ":" .. a.name < b.zone.name .. ":" .. b.name
			else
				return (a.distance or 0) < (b.distance or 0)
			end
		end)
	else
		table.sort(sites, function(a,b)
			return a.zone.name .. ":" .. a.name < b.zone.name .. ":" .. b.name
		end)
	end
	
	for siteNum, site in pairs(sites) do
		local name = site.name
		if name then
			local prefix = ""
			if self:IsSiteBlacklisted(name) then
				prefix = "|cFFFF0000"
			end
			Archy.db.char.digsites.counter[site.name] = Archy.db.char.digsites.counter[site.name] or 0
			local cf = self.frame["digsite" .. siteNum]
			cf.zone:SetText(prefix .. site.zone.name)
			if cf.zone:GetStringWidth() + 20 > zoneWidth then zoneWidth = cf.zone:GetStringWidth() + 20 end
			cf.digsite.name:SetText(prefix .. site.name)
			cf.digsite:SetWidth(cf.digsite.name:GetStringWidth())
			cf.digsite:SetHeight(cf.digsite.name:GetStringHeight())
			if cf.digsite:GetWidth() + 20 > siteWidth then siteWidth = cf.digsite:GetWidth() + 20 end
			cf.digCounter:SetText(prefix .. Archy.db.char.digsites.counter[site.name] .. "/3")
			if cf.digCounter:GetStringWidth() + 10 > digWidth then digWidth = cf.digCounter:GetStringWidth() + 10 end
			if site.race then
				cf.raceID = site.race.id
				cf.raceName = site.race.name
				cf.icon:SetTexture(site.race.texture)
			end
--			cf.icon:SetSize(44,44)
			if not site.distance or site.distance == -1 then
				cf.distance.yards:SetText(prefix .. "...")
			else
				cf.distance.yards:SetText(prefix .. string.format(L["%d yards"], site.distance))
			end
			if cf.distance.yards:GetStringWidth() + 20 > distWidth then distWidth = cf.distance.yards:GetStringWidth() + 20 end
--			cf:SetHeight(22)
			cf:Show()
		end
	end
	self:Resize()
end

function Digsite:ShowHideField(field, width, show)
	if show then
		field:Show()
		field:SetWidth(width)
	else
		field:Hide()
		field:SetWidth(1)
	end
end

function Digsite:Resize()
	for i=1,4 do 
		local cf = self.frame["digsite"..i]
		--cf.filler:SetWidth((siteWidth + 20)- cf.digsite:GetWidth())
		cf.filler:SetWidth(0)
		cf.digsite.name:SetWidth(siteWidth)
		cf.digsite:SetWidth(siteWidth)
		self:ShowHideField(cf.digCounter, digWidth, db.digsites.showCounter)
		self:ShowHideField(cf.zone, zoneWidth, db.digsites.showZone)
		self:ShowHideField(cf.distance, distWidth, db.digsites.showDistance)
	end
	local width = (db.digsites.rowMedia.height*2 - 2) + siteWidth
	if db.digsites.showZone then
		width = width + zoneWidth
	end
	if db.digsites.showCounter then
		width = width + digWidth
	end
	if db.digsites.showDistance then
		width = width + distWidth
	end
	self.frame:SetWidth(width)
end

function Digsite:ShowHideDistance()
	for i=1,4 do
		local cf = self.frame["digsite"..i]
		if db.digsites.showDistance then
			cf.distance:Show()
			cf.distance:SetWidth(20)
		else
			cf.distance:Hide()
			cf.distance:SetWidth(1)
		end
	end
end

function Digsite:ShowHideCounter()
	for i=1,4 do
		local cf = self.frame["digsite"..i]
		if db.digsites.showCounter then
			cf.digCounter:Show()
			cf.digCounter:SetWidth(25)
		else
			cf.digCounter:Hide()
			cf.digCounter:SetWidth(1)
		end
	end
end


function Digsite:UpdateColors()
	self.frame.background:SetTexture(db.digsites.backgroundColor.Red, db.digsites.backgroundColor.Green, db.digsites.backgroundColor.Blue, db.digsites.backgroundColor.Alpha)
	self.frame.headerBackground:SetTexture(db.digsites.headerBackgroundColor.Red, db.digsites.headerBackgroundColor.Green, db.digsites.headerBackgroundColor.Blue, db.digsites.headerBackgroundColor.Alpha)

	local headerFont = Archy.lsm:Fetch("font", db.digsites.headerMedia.font)
	local headerSize = db.digsites.headerMedia.fontSize
	local headerFlags = db.digsites.headerMedia.fontOutline
	self.frame.header.text:SetFont(headerFont, headerSize, headerFlags)
	self.frame.header.text:SetTextColor(db.digsites.headerMedia.fontColor.Red, db.digsites.headerMedia.fontColor.Green, db.digsites.headerMedia.fontColor.Blue, db.digsites.headerMedia.fontColor.Alpha)
	self.frame.header:SetHeight(db.digsites.headerMedia.height)
	self.frame.header.text:SetHeight(db.digsites.headerMedia.height)
	self.frame.headerBackground:SetHeight(db.digsites.headerMedia.height)
	self.frame.header:ClearAllPoints()
	self.frame.header:SetPoint("TOPLEFT", self.frame, "TOPLEFT")
	self.frame.header:SetPoint("TOPRIGHT", self.frame, "TOPRIGHT")
	self.frame.header.text:ClearAllPoints()
	self.frame.header.text:SetPoint("TOPLEFT", self.frame.header, "TOPLEFT", 20, 0)

	local font = Archy.lsm:Fetch("font", db.digsites.rowMedia.font)
	local size = db.digsites.rowMedia.fontSize
	local flags = db.digsites.rowMedia.fontOutline
	local color = db.digsites.rowMedia.fontColor
	local topPoint = self.frame.header
	local bgHeight = 0
	local iconSpacing = db.digsites.rowMedia.height * -0.5 -- -0.342
	for i = 1,4 do
		local cf = self.frame["digsite"..i]
		cf:ClearAllPoints()
		cf:SetPoint("TOPLEFT", topPoint, "BOTTOMLEFT", 0, -3)
		
		cf.digCounter:SetFont(font, size, flags)
		cf.digCounter:SetTextColor(color.Red, color.Green, color.Blue, color.Alpha)
		cf.digsite.name:SetFont(font, size, flags)
		cf.digsite.name:SetTextColor(color.Red, color.Green, color.Blue, color.Alpha)
		cf.distance.yards:SetFont(font, size, flags)
		cf.distance.yards:SetTextColor(color.Red, color.Green, color.Blue, color.Alpha)
		cf.zone:SetFont(font, size, flags)
		cf.zone:SetTextColor(color.Red, color.Green, color.Blue, color.Alpha)
		cf.icon:SetHeight(db.digsites.rowMedia.height*2)
		cf.icon:SetWidth(db.digsites.rowMedia.height*2)
		cf.digsite:ClearAllPoints()
		cf.digsite:SetPoint("LEFT", cf.icon, "RIGHT", iconSpacing, 10)
--		cf.icon:ClearAllPoints()
--		cf.icon:SetPoint("LEFT", cf.digCounter, "RIGHT", iconSpacing, -6)
		cf:SetHeight(db.digsites.rowMedia.height)		
		bgHeight = bgHeight + db.digsites.rowMedia.height + 3
		topPoint = cf
	end
	self.frame.background:ClearAllPoints()
	self.frame.background:SetPoint("TOPLEFT", self.frame.header, "BOTTOMLEFT")
	self.frame.background:SetPoint("BOTTOMRIGHT", self.frame, "BOTTOMRIGHT")
	self.frame:SetHeight(self.frame.headerBackground:GetHeight() + bgHeight + 5)
--	self.frame.background:SetHeight(bgHeight)
	self:Resize()
end

function Digsite:MoveToPosition()
	self.frame:ClearAllPoints()
	self.frame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", db.digsites.location.x, db.digsites.location.y)
end


function Digsite:CreateListFrame()
	local frame = CreateFrame("Frame", "DigSites", UIParent, "ArchyDigsiteListFrame")
	frame:ClearAllPoints()
	frame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", db.digsites.location.x, db.digsites.location.y)
	frame:SetScale(db.digsites.scale)
	frame:SetAlpha(db.digsites.alpha)

	local hdrBg = frame.header:CreateTexture("ArchyDigsiteHeaderBackground", "BACKGROUND")
	hdrBg:SetPoint("TOPLEFT", frame.header, "TOPLEFT", 0, 0)
	hdrBg:SetPoint("BOTTOMRIGHT", frame.header, "BOTTOMRIGHT", 0, 0)
	hdrBg:SetTexture(0,0,0,0)
	frame.headerBackground = hdrBg

	local bg = frame:CreateTexture("ArchyDigsiteBackground", "BACKGROUND")
	bg:SetPoint("TOPLEFT", frame.header, "BOTTOMLEFT")
	bg:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT")
	bg:SetTexture(0,0,0,0)
	frame.background = bg
	
	frame.header.text:SetText(L["Dig Sites"])
	



	frame:SetScript("OnShow", function(self) Digsite:UpdateListFrame() end)
	frame.header:SetScript("OnMouseDown", function(self, button)
		if not db.general.lock then
			if button == "LeftButton" and not self:GetParent().isMoving then
				self:GetParent():StartMoving();
				self:GetParent().isMoving = true;
			end
		end
	end)
	frame.header:SetScript("OnMouseUp", function(self, button)
		if not db.general.lock then
			if button == "LeftButton" and self:GetParent().isMoving then
				self:GetParent():StopMovingOrSizing();
				self:GetParent().isMoving = false;
				local point, relativeTo, relativePoint, x, y = self:GetParent():GetPoint();
				db.digsites.location.x = x
				db.digsites.location.y = y
			end
		end
	end)
	frame.header:SetScript("OnHide", function(self)
		if ( self:GetParent().isMoving ) then
			self:GetParent():StopMovingOrSizing()
			self:GetParent().isMoving = false
		end
	end)
	
	for i=1,4 do
		local cf = frame["digsite"..i]
		cf:Hide()
		cf:SetHeight(1)
		cf.digsite:RegisterForClicks("LeftButtonUp", "RightButtonUp")
		cf.digsite:SetScript("OnClick", function(self, button, down)
			if button == "LeftButton" then
				local zoneName = self:GetParent().zone:GetText()
				local zoneID = Digsite:GetZoneID(zoneName)
				ShowUIPanel(WorldMapFrame)
				SetMapZoom(GetCurrentMapContinent(), zoneID)
			elseif button == "RightButton" then
				local name = self.name:GetText()
				if strsub(name, 1, 2) == "|c" then
					name = strsub(name, 11)
				end
				if not Digsite:IsSiteBlacklisted(name) then
					Archy.db.char.digsites.blacklist[name] = 1
					self.name:SetText("|cFFFF0000" .. name)
				else
					Archy.db.char.digsites.blacklist[name] = nil
					self.name:SetText(name)
				end
				Digsite:SendMessage("ArchyDigSiteBlacklistNotification")
--				Archy:RefreshMinimapIcons();
--					local nearest = Archy:NearestDigsiteName();
--					if Archy.db.char.useTomTom and not Archy.tomtomFrame and nearest == name then
--						Archy:EnableTomTom();
--					end
			end
		end)
		cf.digsite:SetScript("OnEnter", function(self)
			local name = self.name:GetText()
			if strsub(name, 1, 2) == "|c" then
				name = strsub(name, 11)
			end
			local blacklistTooltip = L["Right-Click to blacklist"]
			if Digsite:IsSiteBlacklisted(name) then
				blacklistTooltip = L["Right-Click to remove from blacklist"]
			end
			
			GameTooltip:SetOwner(self, "ANCHOR_NONE")
			GameTooltip:SetPoint("TOPLEFT", self, "BOTTOMLEFT")
			GameTooltip:ClearLines()
			GameTooltip:AddLine(self.name:GetText(), 1, 1, 0)
			local raceName = self:GetParent().raceName or "..."
			GameTooltip:AddLine(L["Fragment Type: "] .. raceName, 1, 1, 1)
			GameTooltip:AddLine(L["Zone: "] .. self:GetParent().zone:GetText(), 1, 1, 1)
			GameTooltip:AddLine(L["Left-Click to view the zone map"], 0, 1, 0)
			GameTooltip:AddLine(blacklistTooltip, 0, 1, 0)
			GameTooltip:Show()
		end)
		cf.digsite:SetScript("OnLeave", function() GameTooltip:Hide() end)
	end
	return frame
end

function Digsite:GetZoneID(name)
	local zones = { GetMapZones(GetCurrentMapContinent()) }
	for zoneid, zonename in pairs(zones) do
		if zonename == name then
			return zoneid
		end
	end
	return nil
end
